﻿// Type: GalaSoft.MvvmLight.Ioc.SimpleIoc
// Assembly: GalaSoft.MvvmLight.Extras.SL5, Version=0.0.0.0, Culture=neutral, PublicKeyToken=e7f91b7703710fe7
// Assembly location: C:\Program Files (x86)\Laurent Bugnion (GalaSoft)\Mvvm Light Toolkit\Binaries\Silverlight5\GalaSoft.MvvmLight.Extras.SL5.dll

using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace GalaSoft.MvvmLight.Ioc
{
  public class SimpleIoc : IServiceLocator, IServiceProvider
  {
    private readonly object[] _emptyArguments = new object[0];
    private readonly object _syncLock = new object();
    private readonly string _uniqueKey = Guid.NewGuid().ToString();
    private readonly Dictionary<Type, Type> _interfaceToClassMap = new Dictionary<Type, Type>();
    private readonly Dictionary<Type, Dictionary<string, Delegate>> _factories = new Dictionary<Type, Dictionary<string, Delegate>>();
    private readonly Dictionary<Type, Dictionary<string, object>> _instancesRegistry = new Dictionary<Type, Dictionary<string, object>>();
    private static SimpleIoc _default;

    public static SimpleIoc Default
    {
      get
      {
        return SimpleIoc._default ?? (SimpleIoc._default = new SimpleIoc());
      }
    }

    public bool Contains<TClass>()
    {
      return this.Contains<TClass>((string) null);
    }

    public bool Contains<TClass>(string key)
    {
      Type key1 = typeof (TClass);
      if (!this._instancesRegistry.ContainsKey(key1))
        return false;
      if (string.IsNullOrEmpty(key))
        return true;
      else
        return this._instancesRegistry[key1].ContainsKey(key);
    }

    public bool IsRegistered<T>()
    {
      return this._interfaceToClassMap.ContainsKey(typeof (T));
    }

    public bool IsRegistered<T>(string key)
    {
      Type key1 = typeof (T);
      if (!this._interfaceToClassMap.ContainsKey(key1) || !this._factories.ContainsKey(key1))
        return false;
      else
        return this._factories[key1].ContainsKey(key);
    }

    public void Register<TInterface, TClass>() where TClass : class, TInterface
    {
      lock (this._syncLock)
      {
        Type local_0 = typeof (TInterface);
        Type local_1 = typeof (TClass);
        if (this._interfaceToClassMap.ContainsKey(local_0))
        {
          if (this._interfaceToClassMap[local_0] != local_1 && this._instancesRegistry.ContainsKey(local_0))
            this._instancesRegistry.Remove(local_0);
          this._interfaceToClassMap[local_0] = local_1;
        }
        else
          this._interfaceToClassMap.Add(local_0, local_1);
        if (!this._factories.ContainsKey(local_0))
          return;
        this._factories.Remove(local_0);
      }
    }

    public void Register<TClass>() where TClass : class
    {
      lock (this._syncLock)
      {
        Type local_0 = typeof (TClass);
        if (local_0.IsInterface)
          throw new ArgumentException("An interface cannot be registered alone");
        if (this._interfaceToClassMap.ContainsKey(local_0))
          this._interfaceToClassMap[local_0] = (Type) null;
        else
          this._interfaceToClassMap.Add(local_0, (Type) null);
        if (!this._factories.ContainsKey(local_0))
          return;
        this._factories.Remove(local_0);
      }
    }

    public void Register<TClass>(Func<TClass> factory) where TClass : class
    {
      this.Register<TClass>(factory, this._uniqueKey);
    }

    public void Register<TClass>(Func<TClass> factory, string key) where TClass : class
    {
      lock (this._syncLock)
      {
        Type local_0 = typeof (TClass);
        if (this._interfaceToClassMap.ContainsKey(local_0))
          this._interfaceToClassMap[local_0] = (Type) null;
        else
          this._interfaceToClassMap.Add(local_0, (Type) null);
        if (this._instancesRegistry.ContainsKey(local_0) && this._instancesRegistry[local_0].ContainsKey(key))
          this._instancesRegistry[local_0].Remove(key);
        if (this._factories.ContainsKey(local_0))
        {
          if (this._factories[local_0].ContainsKey(key))
            this._factories[local_0][key] = (Delegate) factory;
          else
            this._factories[local_0].Add(key, (Delegate) factory);
        }
        else
        {
          Dictionary<string, Delegate> local_1 = new Dictionary<string, Delegate>()
          {
            {
              key,
              (Delegate) factory
            }
          };
          this._factories.Add(local_0, local_1);
        }
      }
    }

    public void Reset()
    {
      this._interfaceToClassMap.Clear();
      this._instancesRegistry.Clear();
      this._factories.Clear();
    }

    public void Unregister<TClass>() where TClass : class
    {
      lock (this._syncLock)
      {
        Type local_0 = typeof (TClass);
        if (this._instancesRegistry.ContainsKey(local_0))
          this._instancesRegistry.Remove(local_0);
        if (this._interfaceToClassMap.ContainsKey(local_0))
          this._interfaceToClassMap.Remove(local_0);
        if (!this._factories.ContainsKey(local_0))
          return;
        this._factories.Remove(local_0);
      }
    }

    public void Unregister<TClass>(TClass instance) where TClass : class
    {
      lock (this._syncLock)
      {
        Type local_0 = typeof (TClass);
        if (!this._instancesRegistry.ContainsKey(local_0))
          return;
        Dictionary<string, object> local_1 = this._instancesRegistry[local_0];
        List<KeyValuePair<string, object>> local_2 = Enumerable.ToList<KeyValuePair<string, object>>(Enumerable.Where<KeyValuePair<string, object>>((IEnumerable<KeyValuePair<string, object>>) local_1, (Func<KeyValuePair<string, object>, bool>) (pair => pair.Value == (object) (TClass) instance)));
        for (int local_3 = 0; local_3 < Enumerable.Count<KeyValuePair<string, object>>((IEnumerable<KeyValuePair<string, object>>) local_2); ++local_3)
        {
          string local_4 = local_2[local_3].Key;
          local_1.Remove(local_4);
          if (this._factories.ContainsKey(local_0) && this._factories[local_0].ContainsKey(local_4))
            this._factories[local_0].Remove(local_4);
        }
      }
    }

    public void Unregister<TClass>(string key) where TClass : class
    {
      lock (this._syncLock)
      {
        Type local_0 = typeof (TClass);
        if (this._instancesRegistry.ContainsKey(local_0))
        {
          Dictionary<string, object> local_1 = this._instancesRegistry[local_0];
          List<KeyValuePair<string, object>> local_2 = Enumerable.ToList<KeyValuePair<string, object>>(Enumerable.Where<KeyValuePair<string, object>>((IEnumerable<KeyValuePair<string, object>>) local_1, (Func<KeyValuePair<string, object>, bool>) (pair => pair.Key == key)));
          for (int local_3 = 0; local_3 < Enumerable.Count<KeyValuePair<string, object>>((IEnumerable<KeyValuePair<string, object>>) local_2); ++local_3)
            local_1.Remove(local_2[local_3].Key);
        }
        if (!this._factories.ContainsKey(local_0) || !this._factories[local_0].ContainsKey(key))
          return;
        this._factories[local_0].Remove(key);
      }
    }

    private object DoGetService(Type serviceType, string key)
    {
      lock (this._syncLock)
      {
        if (string.IsNullOrEmpty(key))
          key = this._uniqueKey;
        Dictionary<string, object> local_0;
        if (!this._instancesRegistry.ContainsKey(serviceType))
        {
          if (!this._interfaceToClassMap.ContainsKey(serviceType))
            throw new ActivationException("Type not found in cache: " + serviceType.FullName);
          local_0 = new Dictionary<string, object>();
          this._instancesRegistry.Add(serviceType, local_0);
        }
        else
          local_0 = this._instancesRegistry[serviceType];
        if (local_0.ContainsKey(key))
          return local_0[key];
        object local_1 = (object) null;
        if (this._factories.ContainsKey(serviceType))
        {
          if (this._factories[serviceType].ContainsKey(key))
            local_1 = this._factories[serviceType][key].DynamicInvoke(new object[0]);
          else if (this._factories[serviceType].ContainsKey(this._uniqueKey))
            local_1 = this._factories[serviceType][this._uniqueKey].DynamicInvoke(new object[0]);
        }
        if (local_1 == null)
        {
          ConstructorInfo local_2 = this.GetConstructorInfo(serviceType);
          ParameterInfo[] local_3 = local_2.GetParameters();
          if (local_3.Length == 0)
          {
            local_1 = local_2.Invoke(this._emptyArguments);
          }
          else
          {
            object[] local_4 = new object[local_3.Length];
            foreach (ParameterInfo item_0 in local_3)
              local_4[item_0.Position] = this.GetService(item_0.ParameterType);
            local_1 = local_2.Invoke(local_4);
          }
        }
        local_0.Add(key, local_1);
        return local_1;
      }
    }

    private ConstructorInfo GetConstructorInfo(Type serviceType)
    {
      ConstructorInfo[] constructors = (this._interfaceToClassMap[serviceType] ?? serviceType).GetConstructors();
      if (constructors.Length <= 1)
        return constructors[0];
      ConstructorInfo constructorInfo = (ConstructorInfo) null;
      for (int index = 0; index < constructors.Length; ++index)
      {
        if (Attribute.GetCustomAttribute((MemberInfo) constructors[index], typeof (PreferredConstructorAttribute)) != null)
        {
          constructorInfo = constructors[index];
          break;
        }
      }
      if (constructorInfo == null)
        throw new ActivationException("Cannot build instance: Multiple constructors found but none marked with PreferredConstructor");
      else
        return constructorInfo;
    }

    public object GetService(Type serviceType)
    {
      return this.DoGetService(serviceType, this._uniqueKey);
    }

    public IEnumerable<object> GetAllInstances(Type serviceType)
    {
      if (this._instancesRegistry.ContainsKey(serviceType))
        return (IEnumerable<object>) this._instancesRegistry[serviceType].Values;
      else
        return (IEnumerable<object>) new List<object>();
    }

    public IEnumerable<TService> GetAllInstances<TService>()
    {
      Type key = typeof (TService);
      if (this._instancesRegistry.ContainsKey(key))
        return Enumerable.Select<object, TService>((IEnumerable<object>) this._instancesRegistry[key].Values, (Func<object, TService>) (instance => (TService) instance));
      else
        return (IEnumerable<TService>) new List<TService>();
    }

    public object GetInstance(Type serviceType)
    {
      return this.DoGetService(serviceType, this._uniqueKey);
    }

    public object GetInstance(Type serviceType, string key)
    {
      return this.DoGetService(serviceType, key);
    }

    public TService GetInstance<TService>()
    {
      return (TService) this.DoGetService(typeof (TService), this._uniqueKey);
    }

    public TService GetInstance<TService>(string key)
    {
      return (TService) this.DoGetService(typeof (TService), key);
    }
  }
}
